package org.lex.ftp.ui.speed;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.IOException;

import javax.swing.Timer;

import org.lex.filter.Filter;
import org.lex.ftp.FTP;
import org.lex.ftp.ui.find.old.speed.SearchResult;
import org.lex.io.IO;
import org.lex.io.IntConst;
import org.lex.net.ftp.FTPURL;
import org.lex.net.ftp.IFTPFile;
import org.lex.net.ftp.LexFTPClient;
import org.lex.net.ftp.util.FTPClientFactory;
import org.lex.net.log.Log.Type;
import org.lex.utils.text.SizeFormat;


public class FileFinder {
	private static final int bufferSize = 32 * 1024;
	private static final int testTime = 3000;
	private FTPURL url = null;
	private LexFTPClient ftp = null;
	private boolean isConnected = false;
	private boolean timeOut = false;
	// =============================
	private IFTPFile testFile = null;
	private SearchResult result = null;

	public FileFinder(FTPURL ftpUrl) {
		super();
		this.url = ftpUrl;
		this.ftp = FTPClientFactory.getDefaultFactory().newFTPClient();
		// this.ftp.setKeepAlive( false );
	}

	public SearchResult getResult() {
		if (null == this.result) {
			this.result = new SearchResult();
			this.result.url = this.url;
		}
		return result;
	}

	public boolean open() {
		log(Type.MISC, "Connecting ...");
		try {
			ftp.openServer(url.getHost(), url.getPort());
			ftp.login(url.getAuthentication());
			this.isConnected = true;
			log(Type.REPLY, "Connected");
			return true;
		} catch (IOException e) {
			log(Type.ERROR, "Connection Failed: " + e.getMessage());
			this.cleanup();
			return false;
		}
	}

	@SuppressWarnings("unchecked")
	public boolean exist(Filter filter) {
		try {
			if (false == this.isConnected)
				return false;
			ftp.cwd(url.getPath());
			log(Type.MISC, "Trying to find target...");
			IFTPFile[] files = ftp.list();
			// FileListParserUtils.parse( ftp );
			for (IFTPFile file : files) {
				if (false == filter.accept(file))
					continue; // ����ϲ�ѯ����,���������һ��
				// ��ϲ�ѯ��������ӵ�TestResult
				this.getResult().fileEntryList.add(file);
				// ��¼�������ļ����������ٶȵ�ʱ��Ҫ�õ�
				if (null == this.testFile)
					this.testFile = file;
				if (false == file.isDirectory() && file.getSize() >= testFile.getSize())
					this.testFile = file;
			}
			// ��ǰĿ¼�µ������ļ����Ѿ�������ϣ�����
			if (true == this.getResult().fileEntryList.isEmpty()) {
				log(Type.ERROR, "Target not found");
				this.cleanup();
				return false;
			} else {
				log(Type.REPLY, getResult().fileEntryList.size() + " target found");
				return true;
			}
		} catch (IOException e) {
			log(Type.ERROR, "Failed to list[ " + url.getPath() + "]");
			this.cleanup();
			return false;
		}
	}

	public long speed() {
		try {
			log(Type.MISC, "Trying to test speed ...");
			ftp.setKeepAlive(false);
			ftp.binary();
			BufferedInputStream in = null;
			in = new BufferedInputStream(ftp.get(testFile.getName()), bufferSize);
			long complete = 0L;
			int length = 0;
			byte[] buffer = new byte[IntConst.INT_512K];
			new SpeedTimer(testTime).start();
			long startTime = System.currentTimeMillis();
			while (((length = in.read(buffer)) != -1) && (false == timeOut))
				complete += length;
			long totalTime = System.currentTimeMillis() - startTime;
			complete /= totalTime / 1000;
			log(Type.REPLY, "Speed: " + SizeFormat.autoFormat(complete));
			IO.closeQuietly(in);
			this.cleanup();
			return complete;
		} catch (IOException e) {
			log(Type.ERROR, "Failed to test speed: " + e.getMessage());
			e.printStackTrace();
			this.cleanup();
			return 0L;
		}
	}

	public void cleanup() {
		if (null == ftp)
			return;
		if (true == this.isConnected) {
			FTPClientFactory.getDefaultFactory().close(ftp);
			ftp = null;
		}
	}

	private void log(Type type, String message) {
		FTP.getApplication().getGlobalLogger().log(type, this.url, message);
	}

	class SpeedTimer extends Timer implements ActionListener {
		public SpeedTimer(int delay) {
			super(delay, null);
			this.setInitialDelay(delay);
			this.addActionListener(this);
		}

		public void actionPerformed(ActionEvent e) {
			timeOut = true;
			this.stop();
		}
	}
}
